/*
	Copyright (c) 2010 <copyright holders>
	This source code is licensed under The MIT License,
	see the file LICENSE for details.
	*/

#ifndef UNITINVARIANT_HPP
#define UNITINVARIANT_HPP

#include <boost/optional.hpp>
#include <boost/property_tree/ptree_fwd.hpp>
#include <boost/scoped_ptr.hpp>
#include <map>
#include <string>
#include <vector>

#include "HelpDefs.hpp"
#include "Spatial.hpp"
#include "BundleInfo.hpp"

namespace spelprog2 {
namespace sim {

const std::string UNIT_INVARIANTS_FILE = "media/units/units.txt";

struct ProjectileInvariant {
    ProjectileInvariant(const boost::property_tree::ptree & projectileNode);

    DECLARE_READ_REF(std::string, Name);
    DECLARE_READ_REF(std::string, Animation); //looping animation, if any

    DECLARE_READ_REF(std::string, MeshName);
    DECLARE_READ_VAR(float, MeshYaw);
    DECLARE_READ_VAR(float, MeshScale);

    DECLARE_READ_VAR(Int, LaunchSpeed);
};

struct Attack {
    Attack(const boost::property_tree::ptree & attackNode);

    bool firesProjectile() const { return projectile_; }
    const ProjectileInvariant & getProjectile() const { assert(firesProjectile()); return *projectile_; }

    DECLARE_READ_REF(std::string, Name);
    DECLARE_READ_REF(std::string, Animation);

    DECLARE_READ_VAR(Int, TotalTime);
    DECLARE_READ_VAR(Int, EffectTime);
    DECLARE_READ_VAR(Int, RelativeFrequency);
    
    DECLARE_READ_VAR(Int, ArcWidth);
    DECLARE_READ_VAR(Int, ArcOrigin);
    DECLARE_READ_VAR(Int, Range);

    DECLARE_READ_VAR(Int, HealthDamageMin);
    DECLARE_READ_VAR(Int, HealthDamageMax);
    DECLARE_READ_VAR(Int, MoraleDamageMin);
    DECLARE_READ_VAR(Int, MoraleDamageMax);

private:
    boost::optional<ProjectileInvariant> projectile_;
};


struct UnitInvariant {
    UnitInvariant(const boost::property_tree::ptree & unitNode);

    const Attack & getAttack(Int index) const   { return attacks_[index]; }
    Int getAttackCount() const                  { return attacks_.size(); }

    DECLARE_READ_REF(std::string, Singular);
    DECLARE_READ_REF(std::string, Plural);

    DECLARE_READ_REF(std::string, MeshName);
    DECLARE_READ_VAR(float, MeshYaw);
    DECLARE_READ_VAR(float, MeshScale);

    DECLARE_READ_VAR(Int, CollisionRadius);
    DECLARE_READ_VAR(Int, MaxSpeed);
    DECLARE_READ_VAR(Int, MaxTurnRate);
    DECLARE_READ_VAR(Int, MaxHealth);
    DECLARE_READ_VAR(Int, HealthRegenRate);
    DECLARE_READ_VAR(Int, MaxMorale);
    DECLARE_READ_VAR(Int, MoraleRegenRate);
    DECLARE_READ_VAR(Int, Persistence);

private:
    std::vector<Attack> attacks_;
};


class UnitInvariants
{
    typedef std::map<std::string, UnitInvariant> InvariantMap;
    InvariantMap invariants_;

    UnitInvariants(const std::string & filename);
public:
    static const UnitInvariants & getInstance()
    {
        static boost::scoped_ptr<UnitInvariants> instance;
        if (!instance) {
            instance.reset(new UnitInvariants(getBundlePath(RESOURCE_DEVIATION) + UNIT_INVARIANTS_FILE));
        }
        return *instance.get();
    }

    const UnitInvariant & find(const std::string & key) const
    {
        InvariantMap::const_iterator it = invariants_.find(key);
        if (it == invariants_.end()) {
            throw Exception(0, std::string("No unit invariant called '") + key + "'", WHERE);
        }
        return it->second;
    }
};

}; //sim
}; //spelprog2

#endif //UNITINVARIANT_HPP
